package com.shakespace.artofandroid.chapter02.remote

import android.app.Service
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Binder
import android.os.IBinder
import android.os.Parcel
import android.os.RemoteCallbackList
import android.util.Log
import com.shakespace.artofandroid.IBookManager
import com.shakespace.artofandroid.IOnNewBookAddedListener
import com.shakespace.artofandroid.chapter02.Book
import com.shakespace.firstlinecode.global.TAG
import java.util.concurrent.CopyOnWriteArrayList

class BookService : Service() {

//    private var listeners = CopyOnWriteArrayList<IOnNewBookAddedListener>()

    // 2020-01-09 NOTE: RemoteCallbackList can help to remove listener in external process , but it is not List , so can't use it as List.
    private var listeners = RemoteCallbackList<IOnNewBookAddedListener>()

    // 2020-01-09 NOTE: CopyOnWriteArrayList适合使用在读操作远远大于写操作的场景里，比如缓存。发生修改时候做copy，新老版本分离，保证读的高性能，适用于以读为主的情况。
    private var books = CopyOnWriteArrayList<Book>()

    private val binder = object : IBookManager.Stub() {
        override fun registerListener(listener: IOnNewBookAddedListener?) {
//            if (!listeners.contains(listener)) {
//                listeners.add(listener)
//            }
            listeners.register(listener)
            printSize(listeners)
        }

        // transmit of An Object between two process , basically by  serialization and deserialization
        // 2020-01-09 NOTE: in multi process , binder will recreate the Object , so there are not the same in memory .
        override fun unregisterListener(listener: IOnNewBookAddedListener?) {
            Log.e(this.TAG, "unregisterListener: in service")
//            if (listeners.contains(listener)) {
//                listeners.remove(listener)
//            }else{
//                Log.e(this.TAG, "unregisterListener: can't find $listener")
//            }
            listeners.unregister(listener)
            printSize(listeners)
        }

        override fun getBookList(): MutableList<Book> {
            return books
        }

        override fun addBook(book: Book) {
            books.add(book)
            onNewBookAdded(book)
        }

//        override val bookList: List<Book>
//            get() {
//                return books
//            }

        override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean {
            // can check permission here also
            val packagesForUid = packageManager.getPackagesForUid(Binder.getCallingUid())
            packagesForUid?.let {
                // 2020-01-09 NOTE: we can check package name here .
                Log.e(this.TAG, "onTransact: ${packagesForUid.get(0)}")// onTransact: com.shakespace.artofandroid
            }
            return super.onTransact(code, data, reply, flags)
        }

    }

    private fun onNewBookAdded(book: Book) {
//        listeners.forEach {
//            it.onNewBookAddedListener(book)
//            Log.e(this.TAG, "onNewBookAdded: notify $it")
//        }

        val size = listeners.beginBroadcast()
        for(i in 0 until size){
            Log.e(this.TAG, "onNewBookAdded: $i")
            val listener = listeners.getBroadcastCookie(i) as IOnNewBookAddedListener?
            listener?.onNewBookAddedListener(book)
        }
        listeners.finishBroadcast()
    }

    private fun printSize(remoteCallbackList: RemoteCallbackList<IOnNewBookAddedListener>){
        val size = remoteCallbackList.beginBroadcast()
        Log.e(this.TAG, "printSize: $size")
        remoteCallbackList.finishBroadcast()
    }



    override fun onCreate() {
        super.onCreate()
        Log.e(this.TAG, "onCreate: ")
    }

    override fun onBind(intent: Intent): IBinder? {
        // 2020-01-08 NOTE: test binderDied
        Thread(Runnable {
            try {
                Thread.sleep(10000)
            } catch (e: Exception) {
            }
            Log.e(this.TAG, "onBind: --- processn killer")
            android.os.Process.killProcess(android.os.Process.myPid())
        }).start()

        checkCallingOrSelfPermission("custom.permission.ACCESS_BOOK_MANAGER")
            .takeIf { it == PackageManager.PERMISSION_DENIED }
            ?.let {
                Log.e(this.TAG, "onBind: permission deny")
                return null
            } ?: return binder
    }
}
